﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace ArgusLib.Controls
{
	public class ValueChecker<T> : TextChecker
	{
		#region Fields
		TypeConverter typeConverter;
		bool valueChanging = false;
		bool textChanging = false;
		#endregion

		#region Properties
		public override Control Control
		{
			get{return base.Control;}
			set
			{
				if (base.Control != null)
				{
					base.Control.KeyUp -= Control_KeyUp;
					base.Control.LostFocus -= Control_LostFocus;
				}

				base.Control = value;
				if (base.Control != null)
				{
					base.Control.KeyUp += Control_KeyUp;
					base.Control.LostFocus += Control_LostFocus;
				}
			}
		}

		T value;
		public T Value
		{
			get { return this.value; }
			set
			{
				if (this.value.Equals(value) == true)
					return;
				this.value = value;
				this.OnValueChanged(EventArgs.Empty);
			}
		}

		public string LastValidText { get; private set; }
		public bool CurrentTextIsValid { get; private set; }
		#endregion

		#region Constructors
		public ValueChecker()
			:base()
		{
		}

		public ValueChecker(Control control, IEnumerable<CharSet> allowedChars)
			: base(control, allowedChars)
		{
		}
		#endregion

		#region Non-Public Methods
		private void InitializeTypeConverter()
		{
			this.typeConverter = TypeDescriptor.GetConverter(typeof(T));
			if (this.typeConverter == null)
				throw new GenericTypeParameterNotSupportetException("No TypeConverter found for T.");
			if (this.typeConverter.CanConvertFrom(typeof(string)) == false)
				throw new GenericTypeParameterNotSupportetException("The TypeConverter for T must support conversion from string.");
			if (this.typeConverter.CanConvertTo(typeof(string)) == false)
				throw new GenericTypeParameterNotSupportetException("The TypeConverter for T must support conversion to string.");
		}

		protected virtual string ConvertToString(T value)
		{
			if (this.typeConverter == null)
				this.InitializeTypeConverter();
			return this.typeConverter.ConvertToString(value);
		}

		protected virtual bool ConvertFromString(string text, out T value)
		{
			if (this.typeConverter == null)
				this.InitializeTypeConverter();
			try
			{
				value = (T)this.typeConverter.ConvertFromString(text);
				return true;
			}
			catch (Exception exception)
			{
				value = default(T);
				return false;
			}
			throw new Exception();
		}

		protected override void OnTextChanged(EventArgs e)
		{
			base.OnTextChanged(e);
			if (this.valueChanging == true)
				return;
			
			T val;
			if (this.ConvertFromString(this.Control.Text, out val) == true)
			{
				this.textChanging = true;
				this.LastValidText = this.Control.Text;
				this.CurrentTextIsValid = true;
				this.Value = val;
				this.textChanging = false;
			}
			else
			{
				this.CurrentTextIsValid = false;
			}
		}

		void Control_LostFocus(object sender, EventArgs e)
		{
			if (this.CurrentTextIsValid == false)
			{
				this.textChanging = true;
				this.Control.Text = this.LastValidText;
				this.textChanging = false;
			}
		}

		void Control_KeyUp(object sender, KeyEventArgs e)
		{
			if (this.CurrentTextIsValid == true)
				return;
			if (e.KeyCode != Keys.Enter)
				return;

			this.textChanging = true;
			this.Control.Text = this.LastValidText;
			this.textChanging = false;
		}
		#endregion

		#region Events
		public event EventHandler ValueChanged;
		protected virtual void OnValueChanged(EventArgs e)
		{
			if (this.ValueChanged != null)
				this.ValueChanged(this, e);
			if (this.textChanging == true)
				return;
			this.valueChanging = true;
			this.LastValidText = this.ConvertToString(this.Value);
			this.CurrentTextIsValid = true;
			this.Control.Text = this.LastValidText;
			this.valueChanging = false;
		}
		#endregion
	}
}
